package com.ocean.web.util;

import ucar.ma2.Array;
import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.Range;
import ucar.nc2.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * 中国近海范围裁剪变量数据工具类
 */
public class CpjypgSSTL0 {
    // 目标经纬度范围中国近海
    private static final double MIN_LAT = 14.0;   // 14°N
    private static final double MAX_LAT = 42.0;   // 42°N
    private static final double MIN_LON = 105.0;  // 105°E
    private static final double MAX_LON = 132.0;  // 132°E

    /**
     * 测试根据中国近海范围裁剪变量数据
     * @param args
     */
    public static void main(String[] args) {
        // 原文件路径和新文件路径（替换为实际路径）
        String inputPath = "D:\\work\\云平台\\样例数据-检验\\智能网格海温\\NMF_BEN_OSM_CSDT_202509090800_120001_SST_00_L0.nc";
        String outputPath = "D:\\work\\NMF_BEN_OSM_CSDT_202509090800_120001_SST_00_L0_cropped.nc";
        CJNcdatazgjh(inputPath,outputPath);
    }
    /**
     * 根据中国近海范围裁剪变量数据
     */
    public static boolean CJNcdatazgjh(String inputPath, String outputPath){
        NetcdfFile inputFile = null;
        NetcdfFileWriter writer = null;

        try {
            // 1. 打开原NC文件
            inputFile = NetcdfFile.open(inputPath);
            System.out.println("成功打开原文件: " + inputPath);

            // 2. 获取关键变量（经纬度为float类型，变量名根据实际文件调整）
            Variable latVar = inputFile.findVariable("lat");    // 纬度变量（float类型）
            Variable lonVar = inputFile.findVariable("lon");    // 经度变量（float类型）
            Variable sstVar = inputFile.findVariable("sst");    // 海温变量
            Variable timeVar = inputFile.findVariable("time");  // 时间变量（按需处理）

            // 校验变量是否存在
            if (latVar == null || lonVar == null || sstVar == null) {
                throw new RuntimeException("经纬度或海温变量不存在，请检查变量名");
            }

            // 校验经纬度是否为float类型
            if (latVar.getDataType() != DataType.FLOAT || lonVar.getDataType() != DataType.FLOAT) {
                throw new RuntimeException("经纬度变量不是float类型，请检查文件或调整代码");
            }

            // 3. 读取经纬度数据（明确处理float类型，转为float[]）
            float[] lats = readFloatArray(latVar);
            float[] lons = readFloatArray(lonVar);

            // 4. 计算裁剪索引（将float数组转为double用于范围判断，不影响原数据类型）
            int[] latIndices = getRangeIndices(lats, MIN_LAT, MAX_LAT);
            int[] lonIndices = getRangeIndices(lons, MIN_LON, MAX_LON);

            if (latIndices == null || lonIndices == null) {
                throw new RuntimeException("目标范围超出原数据范围");
            }
            int latStart = latIndices[0], latCount = latIndices[1];
            int lonStart = lonIndices[0], lonCount = lonIndices[1];

            // 5. 裁剪经纬度数据（保留float类型）
            Array croppedLat = cropVariable(latVar, new int[]{latStart}, new int[]{latCount});
            Array croppedLon = cropVariable(lonVar, new int[]{lonStart}, new int[]{lonCount});

            // 6. 裁剪海温数据（根据实际维度调整，假设为[time, lat, lon]）
            int[] sstOrigin;
            int[] sstShape;
            if (timeVar != null) {
                sstOrigin = new int[]{0, latStart, lonStart};  // 时间维度全取
                sstShape = new int[]{timeVar.getShape()[0], latCount, lonCount};
            } else {
                sstOrigin = new int[]{latStart, lonStart};     // 无时间维度
                sstShape = new int[]{latCount, lonCount};
            }
            Array croppedSST = cropVariable(sstVar, sstOrigin, sstShape);

            // 7. 创建新NC文件（NetCDF4格式，保留经纬度float类型）
            writer = NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf4, outputPath);
            createNewNCFile(writer, inputFile, latVar, lonVar, timeVar, sstVar,
                    croppedLat, croppedLon, croppedSST, latCount, lonCount);

            System.out.println("新文件生成成功: " + outputPath);
            return true;
        } catch (IOException | InvalidRangeException e) {
            e.printStackTrace();
            return false;
        } finally {
            // 关闭资源
            if (inputFile != null) {
                try { inputFile.close(); } catch (IOException e) { e.printStackTrace(); }
            }
            if (writer != null) {
                try { writer.close(); } catch (IOException e) { e.printStackTrace(); }
            }
        }
    }
    /**
     * 读取float类型数组（明确处理经纬度的float类型）
     */
    private static float[] readFloatArray(Variable var) throws IOException {
        Array array = var.read();
        // 直接转换为float[]（因已提前校验类型，此处转换安全）
        return (float[]) array.copyTo1DJavaArray();
    }

    /**
     * 计算float数组中落在[min, max]范围内的起始索引和长度
     */
    private static int[] getRangeIndices(float[] array, double min, double max) {
        int start = -1, end = -1;
        boolean isAscending = array.length > 1 && array[array.length - 1] > array[0];

        for (int i = 0; i < array.length; i++) {
            double val = array[i];  // 转为double用于范围判断（精度足够）
            // 处理经纬度升序/降序
            if ((isAscending && val >= min && val <= max) || (!isAscending && val <= min && val >= max)) {
                if (start == -1) start = i;
                end = i;
            }
        }

        if (start == -1 || end == -1) return null;
        return new int[]{start, end - start + 1};
    }

    /**
     * 裁剪变量数据（保留原数据类型）
     */
    private static Array cropVariable(Variable var, int[] origin, int[] shape) throws IOException, InvalidRangeException {
        Range[] ranges = new Range[origin.length];
        for (int i = 0; i < origin.length; i++) {
            ranges[i] = new Range(origin[i], origin[i] + shape[i] - 1);
        }
        return var.read(Arrays.asList(ranges));
    }

    /**
     * 创建新NC文件，保留经纬度float类型
     */
    private static void createNewNCFile(NetcdfFileWriter writer, NetcdfFile inputFile,
                                        Variable latVar, Variable lonVar, Variable timeVar, Variable sstVar,
                                        Array croppedLat, Array croppedLon, Array croppedSST,
                                        int newLatSize, int newLonSize) throws IOException, InvalidRangeException {

        // 1. 定义新维度（与原维度名称一致）
        Dimension latDim = writer.addDimension(null, latVar.getDimensions().get(0).getName(), newLatSize);
        Dimension lonDim = writer.addDimension(null, lonVar.getDimensions().get(0).getName(), newLonSize);
        List<Dimension> sstDims;

        // 处理时间维度
        Dimension timeDim = null;
        if (timeVar != null) {
            timeDim = writer.addDimension(null, timeVar.getDimensions().get(0).getName(), timeVar.getShape()[0]);
            sstDims = List.of(timeDim, latDim, lonDim);
        } else {
            sstDims = List.of(latDim, lonDim);
        }

        // 2. 创建新变量（明确保留经纬度的float类型）
        // 纬度变量（float类型）
        Variable newLatVar = writer.addVariable(null, latVar.getShortName(), DataType.FLOAT, latDim.getName());
        copyVariableAttributes(latVar, newLatVar);

        // 经度变量（float类型）
        Variable newLonVar = writer.addVariable(null, lonVar.getShortName(), DataType.FLOAT, lonDim.getName());
        copyVariableAttributes(lonVar, newLonVar);

        // 海温变量（保留原类型）
        Variable newSstVar = writer.addVariable(null, sstVar.getShortName(), sstVar.getDataType(), sstDims);
        copyVariableAttributes(sstVar, newSstVar);

        // 时间变量（保留原类型）
        Variable newTimeVar = null;
        if (timeVar != null) {
            newTimeVar = writer.addVariable(null, timeVar.getShortName(), timeVar.getDataType(), timeDim.getName());
            copyVariableAttributes(timeVar, newTimeVar);
        }

        // 3. 复制全局属性
        for (Attribute attr : inputFile.getGlobalAttributes()) {
            writer.addGlobalAttribute(attr);
        }

        // 4. 创建文件并写入数据
        writer.create();
        writer.write(newLatVar, croppedLat);  // 写入裁剪后的float纬度
        writer.write(newLonVar, croppedLon);  // 写入裁剪后的float经度
        writer.write(newSstVar, croppedSST);
        if (newTimeVar != null) {
            writer.write(newTimeVar, timeVar.read());  // 写入时间数据
        }
    }

    /**
     * 复制变量属性（保留单位、描述等元数据）
     */
    private static void copyVariableAttributes(Variable src, Variable dest) {
        for (Attribute attr : src.getAttributes()) {
            dest.addAttribute(attr);
        }
    }
}